/* MAINMENU.C
 * Main menu and many it's related functions
 *************
 * 29.08.2000 : Toni R„s„nen
 *              modifications/documentation for open source
 *
 */
 
/* vars: backg; landgrand_count etc
   functs: landgrand_timerrout
           font*
           writemid
*/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <conio.h>
#include "mainmenu.h"
#include "keys.h"
#include "gfx2.h"
#include "mpack.h"
#include "mixer.h"
#include "mod.h"
#include "pakfile.h"
#include "timer.h"
#include "version.h"
#include "keylist.h"
#include "sndconf.h"
#include "sound.h"
#include "loader.h"

#define MAPSIZE_CLASSIC 0
#define MAPSIZE_LARGE 1
#define MAPSIZE_LAST 2 // do not use, must be last

char controltypestr[3][20] = { "Keyboard","Joystick 1","Joystick 2" };
char controlkeynames[5][10] = { "FIRE", "UP", "DOWN", "LEFT", "RIGHT" };

mapsizestructtype *mapsize; // map size definitions

mapsizestructtype defaultmapsizes[MAPSIZE_LAST] =
               { { 17+22,205-22,6+2*22,194-2*22,
                   12+22,12+8*22-1, 1+22*2, 1+7*22-1,
                   350, 0.7, 6*9+7,
                   { 0,0,0,0,0,0,0,0,0,
                     0,0,0,0,0,0,0,0,0,
                     0,1,1,1,1,1,1,1,0,
                     0,1,1,1,1,1,1,1,0,
                     0,1,1,1,1,1,1,1,0,
                     0,1,1,1,1,1,1,1,0,
                     0,1,1,1,1,1,1,1,0,
                     0,0,0,0,0,0,0,0,0,
                     0,0,0,0,0,0,0,0,0 } },
                     
                 { 17,205,6,194, // min/max x, min/max y
                   12,12+9*22-1, 1,1+9*22-1, // clip min/max x/y
                   150, 1, 80, // worm frequency
                   { 1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1,                     
                     1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1,
                     1,1,1,1,1,1,1,1,1 } }
               }; //

char plrkeys[5][8];
char plrcontrol[4]; // 0=computer, 1..4=human
char controltype[5]; // 0=keyb
char plrrace[4]; // race nro

int cutthroat; // nonzero for cutthroat game

int sndtickheight;

int timetick_speed,timetick_delay,timetick_freq;

int demogame,exitgame,ingame;
int game_length;

int auctmessagetime;
int citymsgtime;

char *font[5];
int fontofs[5][256];
char fontwidth[5][256];

volatile int landgrand_count,landgrand_accelerate;
volatile int counter,sellcounter;
volatile int delaytime;

mainmenuPFV start_new_game;
mainmenuPFV update_function;
int playerc;
char *backg,*city;
char rivi[80],rivi2[80];
int players;

int joyx[2],joyy[2];
int joybuttx[2][2];
char joybutt[2][2];
char joyactive[2];
char joyfail[2];
char joyrel[2][2];

int joymidx[2],joymidy[2];
int joyminx[2],joyminy[2];
int joymaxx[2],joymaxy[2];

int joydivx[2],joydivy[2];

int Jxt[2],Jyt[2];


void addjoybutt (int pressed, int a, int b)
{  if (pressed)
     { if (joyrel[a][b] == 0)
         { joybutt[a][b] = 1;
           joyrel[a][b] = 1;
         }
       else
         { joybutt[a][b] = 0;
         }
     }
   else
     { joybutt[a][b] = 0;
       joyrel[a][b] = 0;
     }
}

int readjoybutts()
{  unsigned char b,c;

   b = inportb (0x201);
   c = 0;

//   joybutt[0][0] = joybutt[0][1] = joybutt[1][0] = joybutt[1][1] = 0;
   
   if (joyactive[0])
     { addjoybutt (!(b & 0x10), 0,0);
       addjoybutt (!(b & 0x20), 0,1);
 //    joybutt[0][0] = 1;
//       if (!(b & 0x20)) joybutt[0][1] = 1;
     }
   if (joyactive[1])
     {
//     if (!(b & 0x40)) joybutt[1][0] = 1;
//       if (!(b & 0x80)) joybutt[1][1] = 1;
       addjoybutt (!(b & 0x40), 1,0);
       addjoybutt (!(b & 0x80), 1,1);
     }

   return 0;
}


int readjoyabs()
{
   int xt[2],yt[2], t,c;
   unsigned char b;

   readjoybutts();
   
   t = 10000;

   outportb (0x201,0xff);

   xt[0] = Jxt[0];
   yt[0] = Jyt[0];
   xt[1] = Jxt[1];
   yt[1] = Jyt[1];
   Jyt[0] = Jxt[0] = Jyt[1] = Jxt[1] = 0;

   if (joyactive[0]) c = 1;
   if (joyactive[1]) c = 2;

   __asm__("cli");
   
   while (t--)
     { b = inportb (0x201);
       if (b & 1) Jxt[0]++; // joy0 x
       if (b & 2) Jyt[0]++; // joy0 y

       if (b & 4) Jxt[1]++; // joy1 x
       if (b & 8) Jyt[1]++; // joy1 y

       if ( (joyactive[0]) && ( (b & 3) == 0) ) c |= 1;
       if ( (joyactive[1]) && ( (b & 12) == 0) ) c |= 2;

       if (c == 3) break;
     }

   __asm__("sti");

   if (!t)
     { if (c & 1 == 0) joyfail[0] = 1;
       if (c & 2 == 0) joyfail[1] = 1;
     }
     // smooth out
   Jxt[0] = (xt[0] + Jxt[0]) / 2;
   Jyt[0] = (yt[0] + Jyt[0]) / 2;
   Jxt[1] = (xt[1] + Jxt[1]) / 2;
   Jyt[1] = (yt[1] + Jyt[1]) / 2;
}

void readjoy()
{  readjoyabs();

   if (joyactive[0] && !joyfail[0])
     { if (joydivx[0])
         joyx[0] = (Jxt[0] - joymidx[0])*100 / joydivx[0];
       if (joydivy[0])
         joyy[0] = (Jyt[0] - joymidy[0])*100 / joydivy[0];
     }
   if (joyactive[1] && !joyfail[1])
     { if (joydivx[1])
         joyx[1] = (Jxt[1] - joymidx[1])*100 / joydivx[1];
       if (joydivy[1])
         joyy[1] = (Jyt[1] - joymidy[1])*100 / joydivy[1];
     }

}

void set_joystick_midpos(int nro)
{  joyminx[nro] = joymaxx[nro] = joymidx[nro] = Jxt[nro];
   joyminy[nro] = joymaxy[nro] = joymidy[nro] = Jyt[nro];
}

void check_joy_range(int nro)
{  if (Jxt[nro] < joyminx[nro]) joyminx[nro] = Jxt[nro];
   if (Jxt[nro] > joymaxx[nro]) joymaxx[nro] = Jxt[nro];
   if (Jyt[nro] < joyminy[nro]) joyminy[nro] = Jyt[nro];
   if (Jyt[nro] > joymaxy[nro]) joymaxy[nro] = Jyt[nro];
}

void set_calibration_numbers(int nro)
{  joymidx[nro] += joyminx[nro];
   joymidy[nro] += joyminy[nro];
   joydivx[nro] = (joymaxx[nro]-joyminx[nro]) / 2;
   joydivy[nro] = (joymaxy[nro]-joyminy[nro]) / 2;
   
}


int calibrate_joystick(int nro) // 0 or 1
{  int c;
   purakuva ("soundcfg.dat", backg);
   setpale (64,64,64);

   joyactive[nro] = 1; joyfail[nro] = 0;

   // plug in, leave centered

   memmove (ruutu, backg, 64000);

   sprintf (rivi,"Calibrating joystick #%i",nro+1);
   writemid (160,30,rivi);

   writemid (160,90,"Plug joystick in and");
   writemid (160,100,"leave it's handle centered.");

   writemid (160,130,"Then press ENTER");

   flip();

   // check

   // move around a few circles, leave centered

   while (1)
     { if (keybuffer[1])
         { keybuffer[1] = 0;
           joyactive[nro] = 0;
           memset (ruutu,0,64000); flip();
           return 0;
         }

       if (keybuffer[28])
         { keybuffer[28] = 0; break; }
     }

   readjoyabs();

   if (joyfail[nro])
     { memmove (ruutu, backg, 64000);
       writemid (160,70,"ERROR!");
       writemid (160,90,"Unable to detect joystick!");
       writemid (160,130,"Press ENTER");

       while ((!keybuffer[28]) && (!keybuffer[1]))
         c++;

       keybuffer[28] = 0;
       keybuffer[1] = 0;

       memset (ruutu,0,64000); flip();
       return 0;

     }

   readjoyabs();
   readjoyabs();

   set_joystick_midpos(nro);


   //---------------------

   memmove (ruutu, backg, 64000);

   sprintf (rivi,"Calibrating joystick #%i",nro+1);
   writemid (160,30,rivi);

   writemid (160,90,"Now, move joystick around in it's full");
   writemid (160,100,"range of motion few times and press");
   writemid (160,110,"either of joystick's buttons");

   flip();

   while (1)
     {  readjoyabs();
        check_joy_range(nro);

        if (joybutt[nro][0] || joybutt[nro][1])
          break;

        if (keybuffer[1])
          { keybuffer[1] = 0;
            break;
          }
     }

   //--------------------

   set_calibration_numbers(nro);

   memmove (ruutu, backg, 64000);

   sprintf (rivi,"Calibrating joystick #%i",nro+1);
   writemid (160,30,rivi);

   writemid (160,50,"Check range of movement.");
   writemid (160,60,"Press ENTER to accept, or C to recalibrate");

   drawcolor = 15;
   arec (160-50,135-50, 160+50,135+50);

   drawcolor = 0;
   arec (160-50-1,135-50-1, 160+50+1,135+50+1);
   arec (160-50+1,135-50+1, 160+50-1,135+50-1);

   flip();

   memmove (ruutu2, ruutu, 64000);

   while (1)
     {
        readjoy();
        memmove (ruutu+50*320, ruutu2+50*320, 64000-50*320);
        putnpic (160+joyx[nro]/3-4, 135+joyy[nro]/3-4, animspr[ANM_CROSS][0]);

        if (keybuffer[1])
          { keybuffer[1] = 0; return 0; }
        if (keybuffer[28])
          { keybuffer[28] = 0; return 0; }
        if (keybuffer[46])
          { keybuffer[46] = 0; return 1; }

        flip();
     }


   memset (ruutu,0,64000); flip();

   return 0;
}










/*
static int iflip_c;
static char cflip_c;
static float fflip_c;
*/

typedef float matrixtype[4][4];
typedef struct { int sx[4],sy[4];
                 float tx[4],ty[4];
                 float tz[4];
                 char *text;
                 float shad[4];
                 int txsize,tysize;
               } polytype;

//static float facenormalx[200],facenormaly[200],facenormalz[200];
static float vertnormalx[200],vertnormaly[200],vertnormalz[200];

static polytype poly;
static int poly_pofs2[2][200];
static char *mmcolortable,*planettex,*moontex;
static volatile matrixtype planetm,moonm;
static int vertc;

static float vertx[230],verty[230],vertz[230];
static float textx[230],texty[230];
static int sxp[230],syp[230];
static int shade[230];

static matrixtype unitmatrix = { {1,0,0,0}, {0,1,0,0}, {0,0,1,0}, { 0,0,0,1} };
static volatile matrixtype calcmatrix;
static volatile matrixtype camera;     // define camera, usually attached to an object...
static volatile matrixtype dispcam;    // displaycamera - position origoed
//static volatile matrixtype worldmatx;
static volatile matrixtype dmat;
static float camx,camy,camz;
static float cx,cy,cz;
static int scrx,scry;
//static int x3d[100],y3d[100],z3d[100];

// triangles used to form planet's surface
#define PLANETTRIC 168
static char trilist[PLANETTRIC*3] = { 0,1,2, 0,2,3, 0,3,4, 0,4,5, 0,5,6, 0,6,7, 0,7,8, 0,8,9,
                               0,9,10, 0,10,11, 0,11,12, 0,12,13, 0,13,14, 0,14,1,
                                  // 1..14 / 15..28
                               1,15,16, 2,16,17, 3,17,18, 4,18,19, 5,19,20, 6,20,21, 7,21,22,
                               8,22,23, 9,23,24, 10,24,25,11,25,26, 12,26,27, 13,27,28, 14,28,15,
                               1,16,2, 2,17,3, 3,18,4, 4,19,5, 5,20,6, 6,21,7, 7,22,8, 8,23,9,
                               9,24,10, 10,25,11, 11,26,12, 12,27,13, 13,28,14, 14,15,1,
                                  // 15..28 / 29..42
                               15,29,30, 16,30,31, 17,31,32, 18,32,33, 19,33,34, 20,34,35, 21,35,36,
                               22,36,37, 23,37,38, 24,38,39, 25,39,40, 26,40,41, 27,41,42, 28,42,29,
                               15,30,16, 16,31,17, 17,32,18, 18,33,19, 19,34,20, 20,35,21, 21,36,22,
                               22,37,23, 23,38,24, 24,39,25, 25,40,26, 26,41,27, 27,42,28, 28,29,15,
                                  // 29..42 / 43..56
                               29,43,44, 30,44,45, 31,45,46, 32,46,47, 33,47,48, 34,48,49, 35,49,50,
                               36,50,51, 37,51,52, 38,52,53, 39,53,54, 40,54,55, 41,55,56, 42,56,43,
                               29,44,30, 30,45,31, 31,46,32, 32,47,33, 33,48,34, 34,49,35, 35,50,36,
                               36,51,37, 37,52,38, 38,53,39, 39,54,40, 40,55,41, 41,56,42, 42,43,29,
                                  // 43..56 / 57..70
                               43,57,58, 44,58,59, 45,59,60, 46,60,61, 47,61,62, 48,62,63, 49,63,64,
                               50,64,65, 51,65,66, 52,66,67, 53,67,68, 54,68,69, 55,69,70, 56,70,57,
                               43,58,44, 44,59,45, 45,60,46, 46,61,47, 47,62,48, 48,63,49, 49,64,50,
                               50,65,51, 51,66,52, 52,67,53, 53,68,54, 54,69,55, 55,70,56, 56,57,43,
                                  // 57..70 / 71..84
                               57,71,72, 58,72,73, 59,73,74, 60,74,75, 61,75,76, 62,76,77, 63,77,78,
                               64,78,79, 65,79,80, 66,80,81, 67,81,82, 68,82,83, 69,83,84, 70,84,71,
                               57,72,58, 58,73,59, 59,74,60, 60,75,61, 61,76,62, 62,77,63, 63,78,64,
                               64,79,65, 65,80,66, 66,81,67, 67,82,68, 68,83,69, 69,84,70, 70,71,57,
                                  // south pole area
                               85,72,71, 85,73,72, 85,74,73, 85,75,74, 85,76,75, 85,77,76, 85,78,77,
                               85,79,78, 85,80,79, 85,81,80, 85,82,81, 85,83,82, 85,84,83, 85,71,84 };





//****************************************************************
// (M1 x M2) multiply two 4x4 matrixes; TR *MAY*NOT* equal to either M1 or M2 !!
void matmul4 (matrixtype *m1, matrixtype *m2, matrixtype *tr)
{  int a,b,c;
   float t;
   for (a = 0; a < 4; a++)
     for (b = 0; b < 4; b++)
       {  t = 0;
          for (c = 0; c < 4; c++)
            t += ( ((*m1)[a][c]) * ((*m2)[c][b])  );
          (*tr)[a][b] = t;
       }
}

//****************************************************************
// (M1 x M2) same as above, but tr may equal either to m1 to m2
void matmul42 (volatile matrixtype *m1, volatile matrixtype *m2, volatile matrixtype *tr)
{  matrixtype targ;
   int a,b,c;
   float t;
   for (a = 0; a < 4; a++)
     for (b = 0; b < 4; b++)
       {  t = 0;
          for (c = 0; c < 4; c++)
            t += (((*m1)[a][c]) * ((*m2)[c][b]));
          targ[a][b] = t;
       }
   memmove ( tr, &targ, sizeof(unitmatrix) );
}

//****************************************************************
//multiply M1 x M2^T (multiply M1 by transpose of M2)
void matmul4inv (volatile matrixtype *m1, volatile matrixtype *m2, volatile matrixtype *tr)
{
   int a,b,c;
   float t;
   for (a = 0; a < 4; a++)
     for (b = 0; b < 4; b++)
       {  t = 0;
          for (c = 0; c < 4; c++)
            t += (((*m1)[a][c]) * ((*m2)[b][c]));
          (*tr)[a][b] = t;
       }
}

//****************************************************************
// calculate matrix angles; does not reset positions
void calc_angles (float angx,float angy,float angz,volatile matrixtype *q)
{  float sx,sy,sz,cx,cy,cz;
   sx = sin(angx); cx = cos(angx);
   sy = sin(angy); cy = cos(angy);
   sz = sin(angz); cz = cos(angz);
   (*q)[0][0] = cy*cz;          (*q)[0][1] = cy*sz;          (*q)[0][2] = -sy;   (*q)[0][3] = 0;
   (*q)[1][0] = sx*sy*cz-cx*sz; (*q)[1][1] = sx*sy*sz+cx*cz; (*q)[1][2] = sx*cy; (*q)[1][3] = 0;
   (*q)[2][0] = cx*sy*cz+sx*sz; (*q)[2][1] = cx*sy*sz-sx*cz; (*q)[2][2] = cx*cy; (*q)[2][3] = 0;
/* (*q)[3][0] = 0;              (*q)[3]81] = 0;              (*q)[3][2] = 0;*/  (*q)[3][3] = 1;
}

/* NOTE! routine uses CALCMATRIX to do calculations! */

/* to simplify routine, define CM to be calcmatrix ... */
#define CM calcmatrix
//****************************************************************
// calculates point with calcmatrix
void calcp (float px,float py,float pz)
{  cx = px*CM[0][0] + py*CM[1][0] + pz*CM[2][0] + CM[3][0];
   cy = px*CM[0][1] + py*CM[1][1] + pz*CM[2][1] + CM[3][1];
   cz = px*CM[0][2] + py*CM[1][2] + pz*CM[2][2] + CM[3][2];

   if (cz < 1) {  scrx = -1; return; }
   scrx = (int)(cx*256 / cz)+(sxsize>>1);
   scry = (int)(cy*256 / cz)+(sysize>>1);
}

void move_obj(volatile matrixtype *m, float ax,float ay, float az, float movx, float movy, float movz)
{  volatile matrixtype movematrix;
   calc_angles (ax,ay,az, &movematrix);
   movematrix[3][0] = movx;
   movematrix[3][1] = movy;
   movematrix[3][2] = movz;

   matmul42 (&movematrix, m, m);
}


//****************************************************************
void prepare_camera(volatile matrixtype *pre_camera)
{  memmove (&dispcam, pre_camera, sizeof(unitmatrix) );
   camx = dispcam[3][0]; dispcam[3][0] = 0;
   camy = dispcam[3][1]; dispcam[3][1] = 0;
   camz = dispcam[3][2]; dispcam[3][2] = 0;
}


void load_colortable()
{  FILE *tied = ffopen ("color.dat","rb");
   if (mmcolortable == NULL)
     mmcolortable = (char*) malloc (16384);
   fread ( mmcolortable, 8192,1, tied);
   ffclose (tied);
}

//#define iflip2(a,b) { iflip_c = a; a = b; b = iflip_c; }
//#define fflip2(a,b) { fflip_c = a; a = b; b = fflip_c; }
//#define cflip2(a,b) { cflip_c = a; a = b; b = cflip_c; }

inline void fflip2 (float *a, float *b)
{  float temp;
   temp = *a;
   *a = *b;
   *b = temp;
}

inline void iflip2 (int *a, int *b)
{  int temp;
   temp = *a;
   *a = *b;
   *b = temp;
}

/*
inline void iflip ( int *i1, int *i2 )
{  int i;
   i = *i1; *i1 = *i2; *i2 = i;
}

inline void fflip ( volatile float *i1, volatile float *i2 )
{  volatile float i;
   i = *i1; *i1 = *i2; *i2 = i;
}

inline void cflip ( char *i1, char *i2 )
{  char i;
   i = *i1; *i1 = *i2; *i2 = i;
}
*/

//-------------------------------------------------------------
void bline (int x1, int y1, int x2, int y2, int c)
{
  int dxx,dyy;
  int x,y,xi,yi;
  int d2,i1,d,i2,co,bx, o,oi,n;

  dxx = (x2-x1); if (dxx < 0) { dxx = -dxx; }
  dyy = (y2-y1); if (dyy < 0) { dyy = -dyy; }

  if (dxx >= dyy) // ---------xdiff > ydiff - "horizontal" line
    { if (x1 > x2)
        { x = x2; y = y2;
          if (y2 > y1) { yi = -1; } else { yi = 1; } }
      else
        { x = x1; y = y1;
          if (y2 > y1) { yi = 1; } else { yi = -1; } }

      oi = yi*sxsize;

      d2 = dyy << 1;
      i1 = d2;
      d = d2-dxx;
      i2 = (dyy-dxx) << 1;
      co = dxx+1;
      bx = d;

//      putp (x,y, drawcolor);

      o = x+y*sxsize;

      poly_pofs2[c][y] = o;

      n = 0;
      while (co > 0)
        {
       //   *(ruutu+(x+y*320)) = drawcolor;

          if (bx < 0)
            { bx = bx+i1; }
          else
            {  if (yi > 0)
                 {  poly_pofs2[c][y] = o; n = 0;
//                    putp (x,y, drawcolor);
                    bx = bx+i2; y = y+yi; o += oi;
                 }
               else
                 {  poly_pofs2[c][y] = o; n = 0;
//                    putp (x,y, drawcolor);
                    bx = bx+i2; y = y+yi; o += oi;
                 }
            }
          co--; x++; o++; n++;
        }
      if ( (n) && (y >= 0) )
        poly_pofs2[c][y] = o;
    }
  else
    { // ------------------ydiff > xdiff - "vertical" line
      if (y1 > y2)
        { x = x2; y = y2; if (x2 > x1) { xi = -1; } else { xi = 1; } }
      else
        { x = x1; y = y1; if (x2 > x1) { xi = 1; } else { xi = -1; } }
      d2 = dxx << 1;
      i1 = d2;
      d = d2-dyy;
      i2 = (dxx-dyy) << 1;
      co = dyy+1;
      bx = d;

      o = x+y*sxsize;
      poly_pofs2[c][y] = o;

      while (co > 0)
        {
        //  *(ruutu+(x+y*320)) = drawcolor;
//          putp (x,y, drawcolor);
          poly_pofs2[c][y] = o;
          if (bx < 0)
            { bx = bx+i1; }
          else
            { bx = bx+i2; /*x = x+xi;*/ o += xi; }
          co--; y++; o += sxsize;
        }
      if (x >= 0)
        poly_pofs2[c][y] = o;
    }
}



//*****************************************************************
void tgtri (polytype *p1)
{  int yc01,yc12,yc02;
   int sy, c,o,yc;
   float d;
   float tz0,tzi0;

   float ptx[3],pty[3];
   float tx0,ty0, tx,ty;
   float txi0,tyi0,txi,tyi;
   float txm,tym;
   float txm2,tym2,tzm2;
   char *text;
   int shad,shad0,shadi0,shadi,shadm,shadm2;
   int pshad[3];

   int o2;
   short w;

   text = (char*) p1->text;

   if (p1->sy[0] > p1->sy[1])
     {  iflip2 (&p1->sx[0],&p1->sx[1]);  iflip2 (&p1->sy[0],&p1->sy[1]);
        fflip2 (&p1->tx[0],&p1->tx[1]);  fflip2 (&p1->ty[0],&p1->ty[1]);
        fflip2 (&p1->shad[0],&p1->shad[1]);  //zbuff
     }
   if (p1->sy[1] > p1->sy[2])
     {  iflip2 (&p1->sx[1],&p1->sx[2]);  iflip2 (&p1->sy[1],&p1->sy[2]);
        fflip2 (&p1->tx[1],&p1->tx[2]);  fflip2 (&p1->ty[1],&p1->ty[2]);
        fflip2 (&p1->shad[1],&p1->shad[2]);  //zbuff
     }
   if (p1->sy[0] > p1->sy[1])
     {  iflip2 (&p1->sx[0],&p1->sx[1]);  iflip2 (&p1->sy[0],&p1->sy[1]);
        fflip2 (&p1->tx[0],&p1->tx[1]);  fflip2 (&p1->ty[0],&p1->ty[1]);
        fflip2 (&p1->shad[0],&p1->shad[1]);  //zbuff
     }

   bline ( p1->sx[0],p1->sy[0], p1->sx[1],p1->sy[1], 1 );
   if (p1->sy[2] != p1->sy[1])
     bline ( p1->sx[2],p1->sy[2], p1->sx[1],p1->sy[1], 1 );
   bline ( p1->sx[2],p1->sy[2], p1->sx[0],p1->sy[0], 0 );

   yc01 = p1->sy[1] - p1->sy[0] + 1;
   yc02 = p1->sy[2] - p1->sy[0] + 1;
   yc12 = p1->sy[2] - p1->sy[1] + 1;

     //zbuff 3 lines below
   ptx[0] = p1->tx[0]; pty[0] = p1->ty[0];
   ptx[1] = p1->tx[1]; pty[1] = p1->ty[1];
   ptx[2] = p1->tx[2]; pty[2] = p1->ty[2];
   
   pshad[0] = (int)(p1->shad[0]*32768);
   pshad[1] = (int)(p1->shad[1]*32768);
   pshad[2] = (int)(p1->shad[2]*32768);

   tx0 = ptx[0];
   ty0 = pty[0];
   shad0 = pshad[0];

   txi0 = (ptx[2]-ptx[0]) / yc02;
   tyi0 = (pty[2]-pty[0]) / yc02;
   shadi0 = (pshad[2]-pshad[0]) / yc02; //zbuff

   if ( (yc12 > 5) || (yc01 < 5) )
     {  d = ((float)(yc01-1) / (float)(yc02));

        txm = ptx[0] + ((ptx[2]-ptx[0]) * d);
        tym = pty[0] + ((pty[2]-pty[0]) * d);
        shadm = pshad[0] + ((pshad[2]-pshad[0]) * d); //zbuff


        c = abs(poly_pofs2[1][p1->sy[1]]-poly_pofs2[0][p1->sy[1]]);
        if (!c) c = 1;
        txi = (ptx[1] - txm) / c;
        tyi = (pty[1] - tym) / c;
        shadi = (pshad[1] - shadm) / c; //zbuff
     }
   else // joitain hassuja efuja, kahden txm:n kautta hitaammin mutta paremmin
     {  yc = (p1->sy[2]-p1->sy[0]) / 2; // puoliv„li

        c = abs(poly_pofs2[1][p1->sy[0]+yc]-poly_pofs2[0][p1->sy[0]+yc]);
        if (!c) c = 1;

        txm2 = tx0 + txi0*yc;
        tym2 = ty0 + tyi0*yc;
        tzm2 = tz0 + tzi0*yc; //n„iss„ 0->2 -v„lin texturepaikka
        shadm2 = shad0 + shadi0*yc; //n„iss„ 0->2 -v„lin texturepaikka

        d = (float)(yc) / (float)(yc01);

        txm = ptx[0] + ((ptx[1]-ptx[0]) * d);
        tym = pty[0] + ((pty[1]-pty[0]) * d);
        shadm = pshad[0] + ((pshad[1]-pshad[0]) * d); //zbuff

        txi = (txm-txm2) / c;
        tyi = (tym-tym2) / c;
        shadi = (shadm-shadm2) / c;


     }

   sy = p1->sy[0];
   while (yc02-- > 0)
     {
        o = poly_pofs2[0][sy];
        c = (poly_pofs2[1][sy]-o);
        if (c < 0) c--; else c++;

        tx = tx0;
        ty = ty0;
        shad = shad0;

        while (c > 0)
          {  c--;
             o2 = (( (int)(tx)&255 ) + ( ( (int)(ty)&127)<<8) );
             w = *(mmcolortable+ ((int)(shad>>7)&(31<<8))+ *(text+o2) );
             *((char*)(ruutu)+o) = w;
             o++;
             tx += txi;
             ty += tyi;
             shad += shadi;
          }

        while (c < 0)
          {  c++;
             o2 = (( (int)(tx)&255 ) + ( ( (int)(ty)&127)<<8) );
             w = *(mmcolortable+((int)(shad>>7)&(31<<8))+ *(text+o2) );
             *((char*)(ruutu)+o) = w;
             o--;
             tx += txi;
             ty += tyi;
             shad += shadi;
          }

        tx0 += txi0;
        ty0 += tyi0;
        shad0 += shadi0;
        sy++;
     }

}



void tgclip (polytype *p1)
{  int xmax=0,xmin=0,ymax=0,ymin=0; // how many out
   int c1,c2,c3; // points
   float m,pois,mpois;
   polytype clipp2,clipp3;


   if (p1->sx[0] < 0)  xmin++;
   if (p1->sx[1] < 0)  xmin++;
   if (p1->sx[2] < 0)  xmin++;

   if (p1->sy[0] < 0)  ymin++;
   if (p1->sy[1] < 0)  ymin++;
   if (p1->sy[2] < 0)  ymin++;

   if (p1->sx[0] > 319)  xmax++;
   if (p1->sx[1] > 319)  xmax++;
   if (p1->sx[2] > 319)  xmax++;

   if (p1->sy[0] > 149)  ymax++;
   if (p1->sy[1] > 149)  ymax++;
   if (p1->sy[2] > 149)  ymax++;

   if ( (xmin == 3) || (xmax == 3) || (ymax == 3) || (ymin == 3) )
     return;

   if (!(xmax+xmin+ymax+ymin))
     {
/*        d = abs(p1->tz[0]-p1->tz[1]);
        if (abs(p1->tz[2]-p1->tz[1]) > d) d = (p1->tz[2]-p1->tz[1]);
        if (abs(p1->tz[2]-p1->tz[0]) > d) d = (p1->tz[2]-p1->tz[1]); */
/*        if (d > 16)
          tgtriPC (p1);
        else */
          tgtri (p1);
        return;
     }


// -----------------------------------------------------------------------
   c1 = c2 = c3 = 0;
   if (xmin == 1)
     {  if (p1->sx[0] < 0) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sx[1] < 0) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sx[2] < 0) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sx[c1]-p1->sx[c2]; pois = -p1->sx[c2];
        mpois = pois/m;
        clipp2.sx[c1] = 0;
        clipp2.sy[c1] = p1->sy[c2] + ((p1->sy[c1]-p1->sy[c2])*mpois);
        clipp2.tx[c1] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c1] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c1] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);
        memmove ( &clipp3, &clipp2, sizeof(polytype) );
        tgclip (&clipp2);

        m = p1->sx[c1]-p1->sx[c3]; pois = -p1->sx[c3];
        mpois = pois/m;
        clipp3.sx[c2] = 0;
        clipp3.sy[c2] = p1->sy[c3] + ((p1->sy[c1]-p1->sy[c3])*mpois);
        clipp3.tx[c2] = p1->tx[c3] + ((p1->tx[c1]-p1->tx[c3])*mpois);
        clipp3.ty[c2] = p1->ty[c3] + ((p1->ty[c1]-p1->ty[c3])*mpois);
        clipp3.tz[c2] = p1->tz[c3] + ((p1->tz[c1]-p1->tz[c3])*mpois);
        tgclip (&clipp3);
        return;
     }

   if (xmin == 2) // two points: clip to one new poly
     {  if (p1->sx[0] >= 0) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sx[1] >= 0) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sx[2] >= 0) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sx[c1]-p1->sx[c2]; pois = -p1->sx[c2];
        mpois = pois/m;
        clipp2.sx[c2] = 0;
        clipp2.sy[c2] = p1->sy[c2] + ((p1->sy[c1]-p1->sy[c2])*mpois);
        clipp2.tx[c2] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c2] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c2] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);

        m = clipp2.sx[c1]-clipp2.sx[c3]; pois = -clipp2.sx[c3];
        mpois = pois/m;
        clipp2.sx[c3] = 0;
        clipp2.sy[c3] = clipp2.sy[c3] + ((clipp2.sy[c1]-clipp2.sy[c3])*mpois);
        clipp2.tx[c3] = clipp2.tx[c3] + ((clipp2.tx[c1]-clipp2.tx[c3])*mpois);
        clipp2.ty[c3] = clipp2.ty[c3] + ((clipp2.ty[c1]-clipp2.ty[c3])*mpois);
        clipp2.tz[c3] = clipp2.tz[c3] + ((clipp2.tz[c1]-clipp2.tz[c3])*mpois);
        tgclip (&clipp2);
        return;
     }

// -----------------------------------------------------------------------
   if (xmax == 1)
     {  if (p1->sx[0] > xclipmax) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sx[1] > xclipmax) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sx[2] > xclipmax) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sx[c1]-p1->sx[c2]; pois = xclipmax-p1->sx[c2];
        mpois = pois/m;
        clipp2.sx[c1] = xclipmax;
        clipp2.sy[c1] = p1->sy[c2] + ((p1->sy[c1]-p1->sy[c2])*mpois);
        clipp2.tx[c1] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c1] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c1] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);
        memmove ( &clipp3, &clipp2, sizeof(polytype) );
        tgclip (&clipp2);

        m = p1->sx[c1]-p1->sx[c3]; pois = xclipmax-p1->sx[c3];
        mpois = pois/m;
        clipp3.sx[c2] = xclipmax;
        clipp3.sy[c2] = p1->sy[c3] + ((p1->sy[c1]-p1->sy[c3])*mpois);
        clipp3.tx[c2] = p1->tx[c3] + ((p1->tx[c1]-p1->tx[c3])*mpois);
        clipp3.ty[c2] = p1->ty[c3] + ((p1->ty[c1]-p1->ty[c3])*mpois);
        clipp3.tz[c2] = p1->tz[c3] + ((p1->tz[c1]-p1->tz[c3])*mpois);
        tgclip (&clipp3);
        return;
     }

   if (xmax == 2) // two points: clip to one new poly
     {  if (p1->sx[0] <= xclipmax) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sx[1] <= xclipmax) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sx[2] <= xclipmax) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sx[c1]-p1->sx[c2]; pois = xclipmax-p1->sx[c2];
        mpois = pois/m;
        clipp2.sx[c2] = xclipmax;
        clipp2.sy[c2] = p1->sy[c2] + ((p1->sy[c1]-p1->sy[c2])*mpois);
        clipp2.tx[c2] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c2] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c2] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);

        m = clipp2.sx[c1]-clipp2.sx[c3]; pois = xclipmax-clipp2.sx[c3];
        mpois = pois/m;
        clipp2.sx[c3] = xclipmax;
        clipp2.sy[c3] = clipp2.sy[c3] + ((clipp2.sy[c1]-clipp2.sy[c3])*mpois);
        clipp2.tx[c3] = clipp2.tx[c3] + ((clipp2.tx[c1]-clipp2.tx[c3])*mpois);
        clipp2.ty[c3] = clipp2.ty[c3] + ((clipp2.ty[c1]-clipp2.ty[c3])*mpois);
        clipp2.tz[c3] = clipp2.tz[c3] + ((clipp2.tz[c1]-clipp2.tz[c3])*mpois);
        tgclip (&clipp2);
        return;
     }

// -----------------------------------------------------------------------
   if (ymin == 1)
     {  if (p1->sy[0] < 0) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sy[1] < 0) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sy[2] < 0) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sy[c1]-p1->sy[c2]; pois = -p1->sy[c2];
        mpois = pois/m;
        clipp2.sy[c1] = 0;
        clipp2.sx[c1] = p1->sx[c2] + ((p1->sx[c1]-p1->sx[c2])*mpois);
        clipp2.tx[c1] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c1] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c1] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);
        memmove ( &clipp3, &clipp2, sizeof(polytype) );
        tgclip (&clipp2);

        m = p1->sy[c1]-p1->sy[c3]; pois = -p1->sy[c3];
        mpois = pois/m;
        clipp3.sy[c2] = 0;
        clipp3.sx[c2] = p1->sx[c3] + ((p1->sx[c1]-p1->sx[c3])*mpois);
        clipp3.tx[c2] = p1->tx[c3] + ((p1->tx[c1]-p1->tx[c3])*mpois);
        clipp3.ty[c2] = p1->ty[c3] + ((p1->ty[c1]-p1->ty[c3])*mpois);
        clipp3.tz[c2] = p1->tz[c3] + ((p1->tz[c1]-p1->tz[c3])*mpois);
        tgclip (&clipp3);
        return;
     }

   if (ymin == 2) // two points: clip to one new poly
     {  if (p1->sy[0] >= 0) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sy[1] >= 0) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sy[2] >= 0) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sy[c1]-p1->sy[c2]; pois = -p1->sy[c2];
        mpois = pois/m;
        clipp2.sy[c2] = 0;
        clipp2.sx[c2] = p1->sx[c2] + ((p1->sx[c1]-p1->sx[c2])*mpois);
        clipp2.tx[c2] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c2] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c2] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);

        m = p1->sy[c1]-p1->sy[c3]; pois = -p1->sy[c3];
        mpois = pois/m;
        clipp2.sy[c3] = 0;
        clipp2.sx[c3] = p1->sx[c3] + ((p1->sx[c1]-p1->sx[c3])*mpois);
        clipp2.tx[c3] = p1->tx[c3] + ((p1->tx[c1]-p1->tx[c3])*mpois);
        clipp2.ty[c3] = p1->ty[c3] + ((p1->ty[c1]-p1->ty[c3])*mpois);
        clipp2.tz[c3] = p1->tz[c3] + ((p1->tz[c1]-p1->tz[c3])*mpois);
        tgclip (&clipp2);
        return;
     }

// -----------------------------------------------------------------------
   if (ymax == 1)
     {  if (p1->sy[0] > yclipmax) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sy[1] > yclipmax) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sy[2] > yclipmax) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sy[c1]-p1->sy[c2]; pois = yclipmax-p1->sy[c2];
        mpois = pois/m;
        clipp2.sy[c1] = yclipmax;
        clipp2.sx[c1] = p1->sx[c2] + ((p1->sx[c1]-p1->sx[c2])*mpois);
        clipp2.tx[c1] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c1] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c1] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);
        memmove ( &clipp3, &clipp2, sizeof(polytype) );
        tgclip (&clipp2);

        m = p1->sy[c1]-p1->sy[c3]; pois = yclipmax-p1->sy[c3];
        mpois = pois/m;
        clipp3.sy[c2] = yclipmax;
        clipp3.sx[c2] = p1->sx[c3] + ((p1->sx[c1]-p1->sx[c3])*mpois);
        clipp3.tx[c2] = p1->tx[c3] + ((p1->tx[c1]-p1->tx[c3])*mpois);
        clipp3.ty[c2] = p1->ty[c3] + ((p1->ty[c1]-p1->ty[c3])*mpois);
        clipp3.tz[c2] = p1->tz[c3] + ((p1->tz[c1]-p1->tz[c3])*mpois);
        tgclip (&clipp3);
        return;
     }

   if (ymax == 2) // two points: clip to one new poly
     {  if (p1->sy[0] <= yclipmax) { c1 = 0; c2 = 1; c3 = 2; }
        if (p1->sy[1] <= yclipmax) { c1 = 1; c2 = 0; c3 = 2; }
        if (p1->sy[2] <= yclipmax) { c1 = 2; c2 = 0; c3 = 1; }
        memmove ( &clipp2, p1, sizeof(polytype) );

        m = p1->sy[c1]-p1->sy[c2]; pois = yclipmax-p1->sy[c2];
        mpois = pois/m;
        clipp2.sy[c2] = yclipmax;
        clipp2.sx[c2] = p1->sx[c2] + ((p1->sx[c1]-p1->sx[c2])*mpois);
        clipp2.tx[c2] = p1->tx[c2] + ((p1->tx[c1]-p1->tx[c2])*mpois);
        clipp2.ty[c2] = p1->ty[c2] + ((p1->ty[c1]-p1->ty[c2])*mpois);
        clipp2.tz[c2] = p1->tz[c2] + ((p1->tz[c1]-p1->tz[c2])*mpois);

        m = clipp2.sy[c1]-clipp2.sy[c3]; pois = yclipmax-clipp2.sy[c3];
        mpois = pois/m;
        clipp2.sy[c3] = yclipmax;
        clipp2.sx[c3] = clipp2.sx[c3] + ((clipp2.sx[c1]-clipp2.sx[c3])*mpois);
        clipp2.tx[c3] = clipp2.tx[c3] + ((clipp2.tx[c1]-clipp2.tx[c3])*mpois);
        clipp2.ty[c3] = clipp2.ty[c3] + ((clipp2.ty[c1]-clipp2.ty[c3])*mpois);
        clipp2.tz[c3] = clipp2.tz[c3] + ((clipp2.tz[c1]-clipp2.tz[c3])*mpois);
        tgclip (&clipp2);
        return;
     }


}


/*
void calculate_facenormals()
{  float nx,ny,nz,a;
   float x1,y1,z1, x2,y2,z2, x3,y3,z3;
   int n,i,c,f;

   n = 0; f = 0;
   for (i = 0; i < PLANETTRIC; i++)
     {
        c = trilist[i*3];
        x1 = vertx[c]; y1 = verty[c]; z1 = vertz[c];
        c = trilist[i*3+1];
        x2 = vertx[c]; y2 = verty[c]; z2 = vertz[c];
        c = trilist[i*3+2];
        x3 = vertx[c]; y3 = verty[c]; z3 = vertz[c];

        nx = (y2-y1)*(z3-z1)-(z2-z1)*(y3-y1);
        ny = (z2-z1)*(x3-x1)-(x2-x1)*(z3-z1);
        nz = (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1);
        a = sqrt(nx*nx+ny*ny+nz*nz);
        nx /= a; ny /= a; nz /= a;

        facenormalx[i] = nx;
        facenormaly[i] = ny;
        facenormalz[i] = nz;
     }
}



void calculate_vertexnormals()
{  float nx,ny,nz,a;
   float x1,y1,z1, x2,y2,z2, x3,y3,z3;
   int n,i,c,f,p,v,ve;
   int p1,p2,p3;

   int pcount;
   float px,py,pz;
   int planenormals[20][3];

   n = 0; v = 0;
   ve = 0;
   for (i = 0; i < vertc; i++) // all vertices
     {  pcount = 0;
        px = py = pz = 0;
        for (p = 0; p < PLANETTRIC; p++) // all tris
          if ( (trilist[p*3+0] == i) ||
               (trilist[p*3+1] == i) ||
               (trilist[p*3+2] == i) )
             {
                nx = facenormalx[p];
                ny = facenormaly[p];
                nz = facenormalz[p];
                  // check if this plane is already defined.
                  // CHECK: slight float error in 10th (or so) decimal place???
                  //        affects calculations cumulatively
                a = 0;
                for (f = 0; f < pcount; f++)
                  if ( ((int)(nx*300) == planenormals[f][0]) &&
                       ((int)(ny*300) == planenormals[f][1]) &&
                       ((int)(nz*300) == planenormals[f][2]) )  a = -1;
                  // if plane wasn't already in list, add it to it
                if (a != -1)
                  {  planenormals[pcount][0] = (int)(nx*300);
                     planenormals[pcount][1] = (int)(ny*300);
                     planenormals[pcount][2] = (int)(nz*300);
                     px += nx;
                     py += ny;
                     pz += nz;
                     pcount++;
                  }

             }
        if (pcount) // make sure there _are_ planes attached to vertex!
          {  px /= pcount;
             py /= pcount;
             pz /= pcount;
             a = sqrt (px*px + py*py + pz*pz);

             vertnormalx[i] = px / a;
             vertnormaly[i] = py / a;
             vertnormalz[i] = pz / a;
          }
        else
          {  vertnormalx[i] = 0;
             vertnormaly[i] = 0;
             vertnormalz[i] = 0;
          }
     }
}


void create_planet()
{  int a,b,c, i,n;
   double ra,rb, sa,sb,ca,cb;
   double tx,ty;
   FILE *tied;

#define DOTC 7

   ra = 0; rb = 0; // angles
   c = 0;
   ty = 0;
   for (a = 0; a <= DOTC; a++) // from north to south, 128pix
     {  rb = 0; tx = 0;
        sa = sin(ra);
        ca = cos(ra); 
        for (b = 0; b < DOTC*2; b++) // from east to west, 256pix
          {  sb = sin(rb); cb = cos(rb);
             vertx[c] = (30*sa*cb);
             verty[c] = (30*sa*sb);
             vertz[c] = (30*ca);
             
             textx[c] = tx;
             texty[c] = ty;
             rb += (double)( (M_PI*2) / (DOTC*2));
             c++;
             tx += 256.0/(DOTC*2);
          }
        ra += (3.14159 / DOTC);
        ty += 128.0/DOTC;
       }

       // remove dupe verts, no sense in rotating them too!
   for (i = 0; i < c; i++)
     for (n = i+1; n < c; n++)
       if ( fabs(vertx[i]-vertx[n])<0.1 && fabs(verty[i]-verty[n])<0.1 && fabs(vertz[i]-vertz[n])<0.1 )
         {  for (a = n+1; a < c; a++) // dupe, remove vert #n
              {  vertx[a-1] = vertx[a];
                 verty[a-1] = verty[a];
                 vertz[a-1] = vertz[a];
                 textx[a-1] = textx[a];
                 texty[a-1] = texty[a];
              }
            c--;
            n--;
         }
   vertc = c;

   calculate_facenormals();
   calculate_vertexnormals();

   tied = fopen ("psurf.dat","wb");

   fwrite ( &vertc, 4,1, tied);
   fwrite ( vertx, vertc, sizeof(float), tied);
   fwrite ( verty, vertc, sizeof(float), tied);
   fwrite ( vertz, vertc, sizeof(float), tied);
   fwrite ( vertnormalx, vertc, sizeof(float), tied);
   fwrite ( vertnormaly, vertc, sizeof(float), tied);
   fwrite ( vertnormalz, vertc, sizeof(float), tied);
   fwrite ( textx, vertc, sizeof(float), tied);
   fwrite ( texty, vertc, sizeof(float), tied);

   ffclose (tied);
}
*/

void load_planet()
{  FILE *tied;
   tied = ffopen ("psurf.dat","rb");

   fread ( &vertc, 4,1, tied);
   fread ( vertx, vertc, sizeof(float), tied);
   fread ( verty, vertc, sizeof(float), tied);
   fread ( vertz, vertc, sizeof(float), tied);
   fread ( vertnormalx, vertc, sizeof(float), tied);
   fread ( vertnormaly, vertc, sizeof(float), tied);
   fread ( vertnormalz, vertc, sizeof(float), tied);
   fread ( textx, vertc, sizeof(float), tied);
   fread ( texty, vertc, sizeof(float), tied);

   ffclose (tied);
}

int visible (int x1,int y1, int x2,int y2, int x3,int y3)
{  int a1,a2,a3,a4;

   a1 = x3-x1;
   a2 = y2-y1;
   a3 = x2-x1;
   a4 = y3-y1;

   return a1*a2-a3*a4;
}


int calc_shade (float nx, float ny, float nz)
{
//   return (int)( (nx*CM[0][2] + ny*CM[1][2] + nz*CM[2][2]) * 15);

   float p,vx,vz;
   vx = nx*CM[0][0] + ny*CM[1][0] + nz*CM[2][0];
//   vy = nx*CM[0][1] + ny*CM[1][1] + nz*CM[2][1] + CM[3][1];
   vz = nx*CM[0][2] + ny*CM[1][2] + nz*CM[2][2];

   p = vx*0.7 + vz*0.7;

   return (int)( p * 17);
}

void draw_planet(volatile matrixtype *matx, float scale, char *texture, float xadd)
{  int i,p1,p2,p3;


   prepare_camera(&camera);
   memmove ( &dmat, matx, sizeof(unitmatrix));
   dmat[3][0] -= camx;
   dmat[3][1] -= camy;
   dmat[3][2] -= camz;
   matmul4inv (&dmat, &dispcam, &calcmatrix);


   for (i = 0; i < vertc; i++)
     {  calcp ( vertx[i]*scale+xadd, verty[i]*scale, vertz[i]*scale );

        sxp[i] = scrx;
        syp[i] = scry;

        shade[i] = -calc_shade (vertnormalx[i],vertnormaly[i],vertnormalz[i]);
        if (shade[i] < 3) shade[i] = 3;
     }


   poly.text = texture;
   poly.txsize = 1;
   poly.tysize = 1;

   for (i = 0; i < PLANETTRIC; i++)
     {  p1 = trilist[i*3+0];
        p2 = trilist[i*3+1];
        p3 = trilist[i*3+2];
        if (visible ( sxp[p1],syp[p1], sxp[p2],syp[p2], sxp[p3],syp[p3]) > 0)
          {  /*aline ( sxp[p1],syp[p1], sxp[p2],syp[p2] );
             aline ( sxp[p2],syp[p2], sxp[p3],syp[p3] );
             aline ( sxp[p3],syp[p3], sxp[p1],syp[p1] ); */

             poly.sx[0] = sxp[p1];   poly.sy[0] = syp[p1];
             poly.sx[1] = sxp[p2];   poly.sy[1] = syp[p2];
             poly.sx[2] = sxp[p3];   poly.sy[2] = syp[p3];
             poly.tx[0] = textx[p1]; poly.ty[0] = texty[p1];
             poly.tx[1] = textx[p2]; poly.ty[1] = texty[p2];
             poly.tx[2] = textx[p3]; poly.ty[2] = texty[p3];

             if ( (poly.tx[0] == 0) && (poly.tx[1] > 200) ) poly.tx[0] = 255;
             if ( (poly.tx[0] == 0) && (poly.tx[2] > 200) ) poly.tx[0] = 255;

             if ( (poly.tx[1] == 0) && (poly.tx[0] > 200) ) poly.tx[1] = 255;
             if ( (poly.tx[1] == 0) && (poly.tx[2] > 200) ) poly.tx[1] = 255;

             if ( (poly.tx[2] == 0) && (poly.tx[0] > 200) ) poly.tx[2] = 255;
             if ( (poly.tx[2] == 0) && (poly.tx[1] > 200) ) poly.tx[2] = 255;

             poly.shad[0] = shade[p1];
             poly.shad[1] = shade[p2];
             poly.shad[2] = shade[p3];

//             tgtri (&poly);
             tgclip (&poly);
          }
     }
}

void writesel (int sel,int issel, char* rivi2)
{  if (sel == issel)
     sprintf (rivi,"• %s –",rivi2);
   else
     strcpy (rivi, rivi2);
}


#define HF_TITLE 1
#define HF_TEXT 2
#define HF_PAGEEND -1
void disp_page(int page)
{  int i,y,s;
   FILE *tied;
   y = 30;

   spritedest = ruutu;
   memmove (ruutu,backg,64000);
   
   tied = ffopen ("helpt.dat", "rb");

   ffseek (tied, page*4, SEEK_SET);
   fread (&i, 4,1, tied);

   if (i == 0) return;

   ffseek (tied, i, SEEK_SET);

   fread (&s, 4,1,tied);
   while (s != HF_PAGEEND)
     {  if (s == HF_TITLE)
          {  fread (&i, 4,1, tied);
             fread (rivi, i,1, tied);
             writef (15,15, rivi,1);
          }
        if (s == HF_TEXT)
          {  fread (&i, 4,1, tied);
             fread (rivi, i,1, tied);
             writef (15,y, rivi,1);
             y += 10;
             flip();
          }
        fread (&s, 4,1,tied);
     }

   
   ffclose (tied);
   flip();
}

void mule_helpmain()
{  int page,changed;
   purakuva ("helpb.dat", backg);

   changed = 1;
   page = 1;
   while (1)
     {  if (changed)
          {  disp_page (page);
             changed = 0;
          }
        if (keybuffer[1])
          {  keybuffer[1] = 0;  break; }

        if (keybuffer[75] && page > 1)
          {  page--;
             keybuffer[75] = 0;
             changed++;
          }

        if (keybuffer[77] && page < 10)
          {  page++;
             keybuffer[77] = 0;
             changed++;
          }

//        check_shutdown();

     }
}

void writeselc ( int sel, int y, char *left1, char *right1)
{  char left[50],right[50];
   if (sel)
     {  sprintf (left,"• %s",left1);
        sprintf (right,"%s –",right1);
     }
   else
     {  sprintf (left,"%s",left1);
        sprintf (right,"%s",right1);
     }
   writeleft (155,y, left);
   writef (165,y,right,1);
}

void redraw_config_screen(int sel, int ctrlsel)
{  char rivi2[50];
   int c,c2, k,k2,i;

   memmove (ruutu,backg,64000);

   writemid (160,20,"Control Configuration");

   sprintf (rivi2, "#%i", ctrlsel);
   writeselc ( (sel==1), 65,"Controls :", rivi2);

   writeselc ( (sel==2), 75,"Control type :", controltypestr[(int)controltype[ctrlsel]]);

   switch ( controltype[ctrlsel] )
     { case 0 : writeselc ( (sel==3), 85,"Fire :", keylist[(int)plrkeys[ctrlsel][K_FIRE]]);
                writeselc ( (sel==4), 95,"Up :", keylist[(int)plrkeys[ctrlsel][K_UP]]);
                writeselc ( (sel==5),105,"Down :", keylist[(int)plrkeys[ctrlsel][K_DOWN]]);
                writeselc ( (sel==6),115,"Left :", keylist[(int)plrkeys[ctrlsel][K_LEFT]]);
                writeselc ( (sel==7),125,"Right :", keylist[(int)plrkeys[ctrlsel][K_RIGHT]]);
                break;

       case 1 :
       case 2 : writeselc ( (sel==3), 85,"Calibrate", "");
                break;
     }

   writeselc ( (sel==8),140,"Accept", "");

   switch (sel)
     {  case 1 : strcpy (rivi,"Press LEFT or RIGHT to change controls");break;
        case 2 : strcpy (rivi,"Select keyboard or joysticks"); break;
        case 8 : strcpy (rivi,"Press ENTER to accept controls"); break;
        default: if (controltype[ctrlsel] == 0)
                   strcpy (rivi,"Press ENTER to define new key");
                 else
                   strcpy (rivi,"Press ENTER to calibrate joystick");
                 break;
     }

   writemid (160,180,rivi);

   i = 0;
   for (c = 0; c < 4; c++)
     if (controltype[c] == 0)
       for (c2 = c; c2 < 4; c2++)
         for (k = 0; k < 5; k++)
           for (k2 = 0; k2 < 5; k2++)
             if ( (plrkeys[c][k] == plrkeys[c2][k2]) && (plrkeys[c][k] != 0) && (!i) )
               if ( (c != c2) ||
                   ( (c == c2) && (k != k2) ) ) // if key isn't same
                 {
                    sprintf (rivi,"WARNING! Key for %s in controls #%i is",controlkeynames[k],c);
                    writemid (160,155,rivi);

                    sprintf (rivi,"same as key for %s in controls #%i!",controlkeynames[k2],c2);
                    writemid (160,165,rivi);

                    i++;
                 }

}

/*
void restore_controlconfig()
{  FILE *tied;
   tied = fopen ("gamedata.cfg","rb");
   if (tied == NULL) return; // no controls

   fread (plrkeys, sizeof(plrkeys),1, tied);
   fread (controltype, sizeof(controltype),1, tied);

   fread ( &modvolume, sizeof(int),1, tied);
   fread ( &sfxvolume, sizeof(int),1, tied);

   fclose (tied);
   
}

void store_controlconfig()
{  FILE *tied;
   tied = fopen ("gamedata.cfg","wb");
   if (tied == NULL) return; // ERROR!!!

   fwrite (plrkeys, sizeof(plrkeys),1, tied);
   fwrite (controltype, sizeof(controltype),1, tied);

   fwrite ( &modvolume, sizeof(int),1, tied);
   fwrite ( &sfxvolume, sizeof(int),1, tied);

   fclose (tied);
   
}
*/

void configure_controls()
{  int sel,i,ctrlsel, changed;
   memset (ruutu,0,64000); flip();
   purakuva ("soundcfg.dat", backg);
   setpale (64,64,64);
   ctrlsel = 1;

   sel = 8;
   ctrlsel = 1;
   changed = 1;
   
   while (1)
     {  if (changed)
          {  redraw_config_screen(sel,ctrlsel);
             flip();
             changed = 0;
          }

        if (keybuffer[1])
          { keybuffer[1] = 0; break; } // esc

        if ( (keybuffer[72]) && (sel > 1) ) // up
          {  if (controltype[ctrlsel] == 0)
               sel--;
             else
               if (sel == 8)
                 sel = 3;
               else
                 if (sel > 1) sel--;
             keybuffer[72] = 0; changed++;

          }

        if ( (keybuffer[80]) && (sel < 8) ) // down
          {  keybuffer[80] = 0; changed++;
             if (controltype[ctrlsel] == 0)
               sel++;
             else
               if (sel == 3)
                 sel = 8;
               else
                 if (sel < 3) sel++;
          }

        if ( (keybuffer[75]) )
          {  keybuffer[75] = 0;
             if (ctrlsel == 1) ctrlsel = 4; else ctrlsel--;
             changed++; 
          }

        if ( (keybuffer[77]) )
          {  keybuffer[77] = 0;
             if (ctrlsel == 4) ctrlsel = 1; else ctrlsel++;
             changed++; 
          }

        if (keybuffer[28])
          {  keybuffer[28] = 0;
             changed++; 

             if ( (sel == 3) && (controltype[ctrlsel] > 0) )
               while (calibrate_joystick ( controltype[ctrlsel]-1 ) == 1) ;

             if (sel == 2)
               { controltype[ctrlsel]++;
                 if (controltype[ctrlsel] == 3)
                   controltype[ctrlsel] = 0;
               }

             if ( (sel >= 3) && (sel <= 7) && (controltype[ctrlsel] == 0) ) // new key
               {  for (i = 0; i < 90; i++)
                    keybuffer[i] = 0;
                  plrkeys[ctrlsel][sel-3] = 90;
                  redraw_config_screen(sel,ctrlsel);
                  flip();

                  i = -1;
                  while (i == -1)
                    {  for (i = 90; i > -1; i--)
                         if (keybuffer[i])
                           {  keybuffer[i] = 0;
                              plrkeys[ctrlsel][sel-3] = i;
                              break;
                           }
                    }
               }

             if (sel == 8)
               break;
          } // keybuffer[28]
     }

   memset (ruutu,0,64000); flip();

//   store_config();
//   store_controlconfig();
}

void prepare_joysticks()
{  joyactive[0] = joyactive[1] = 1;
   joyfail[0] = joyfail[1] = 0;
}

void mule_mainmenu()
{  float moonangle;
   char *lampanims[2];
   int lamppic, menu;
   int noact,i, x,y,o;

   char *imganims[33]; // img text animation
   int animphase;

   int sel,action,reload;

   char menuselmax[5] = { 5,5,3,4,0 };

   if (!planettex) planettex = (char*) malloc (128*256);
   if (!moontex) moontex = (char*) malloc (128*256);
   load_colortable();

   memmove (&planetm, &unitmatrix, sizeof(unitmatrix));
   memmove (&moonm, &unitmatrix, sizeof(unitmatrix));
   memmove (&camera, &unitmatrix, sizeof(unitmatrix));

   camera[3][0] = 0;
   camera[3][1] = 0;
   camera[3][2] = -200;
   move_obj (&planetm, -4.5,-0.5,0, 0,0,0);
   move_obj (&moonm, 1.5,0,0.1, 0,0,0);

   sel = 1;

   moonangle = 0;

   counter = 10; lamppic = 0;
   reload = 1; menu = 0;
   noact = 0;
   ingame = 0;
   action = 0;
   animphase = -1;
   counter = 0;
   while (action != 4)
     {  //check_shutdown();
        if (reload)
          {  spritedest = ruutu;
             purakuva ("imganim1.dat", ruutu);

              // load img text animations
             x = y = 1; o = 0;
             for (i = 0; i < 31; i++)
               {  imganims[i] = (city+o);
                  getpic (x,y,x+70,y+9, imganims[i]);
               
                  y += 11; o += (71*10+16);
                  if (i == 17)
                    { x += 72; y = 1; }
               }
          
             purakuva ("planet.dat", planettex);
             purakuva ("moon.dat", moontex);
             load_planet();
             timer_routine = landgrand_timerrout;

             purakuva ("plananm.dat", ruutu);
             spritedest = ruutu;

             purakuva ("planback.dat", backg);
             setpale (64,64,64);

             lampanims[0] = ruutu2;
             getpic (15,14,91,44, lampanims[0]);
             lampanims[1] = ruutu2+2640;
             getpic (93,14,169,44, lampanims[1]);
             reload = 0; noact = 0;
             landgrand_count = 0;
          }
        memmove (ruutu+30*320, backg+30*320, 140*320);
        if (!counter)
          {  lamppic = rand() % 2;
             counter = 10;
          }

        draw_planet(&moonm, 0.2, moontex, 170);
        draw_planet(&planetm, 1, planettex, 0);
        
        move_obj (&planetm, 0,0,0.01*landgrand_count, 0,0,0);
        move_obj (&moonm, 0,0,0.008*landgrand_count, 0,0,0);

        noact += landgrand_count;
        if ( (keybuffer[72]) || (keybuffer[80]) || (keybuffer[28]) )
          noact = 0;
        
        landgrand_count = 0;
        moonangle += 0.005;
        if (moonangle > 2*M_PI) moonangle -= 2*M_PI;

        drawcolor = 0; aline (0,0,0,199);

        memmove (ruutu,backg,31*320);
        memmove (ruutu+170*320,backg+170*320,30*320);
        putnpic (37,0, lampanims[lamppic]);

        sprintf (rivi, "v %s", GAME_VERSION_STR);
        writef (1,192,rivi,2);

        writemid (160,55,"MULE");
        writemid (160,65,"˜ Imagination, 1999, 2000");

          // img logo anim
        if ( (animphase == -1) && (counter == 0) && ( (rand() % 100) == 1) )
          {  animphase = 0;
          }

        if (animphase >= 0)
          {  if (sellcounter == 0)
               {  animphase++;
                  sellcounter = 2;
               }
             if (animphase <= 30)
               putnpic (236,184, imganims[animphase]);
             else
               animphase = -1;
          }

        switch (menu)
          {  case 0 : writesel (sel,1, "START COLONIZING"); writemid (160, 115,rivi);
                      writesel (sel,2, "CONTROL CONFIGURATION"); writemid (160, 125,rivi);
                      writesel (sel,3, "HELP"); writemid (160,135,rivi);
                      writesel (sel,4, "SOUND CONFIGURATION"); writemid (160,145,rivi);
                      writesel (sel,5, "QUIT"); writemid (160,155,rivi);
                      break;
                      
             case 1 : writesel (sel,1, "ONE PLANETEER"); writemid (160, 115,rivi);
                      writesel (sel,2, "TWO PLANETEERS"); writemid (160, 125,rivi);
                      writesel (sel,3, "THREE PLANETEERS"); writemid (160, 135,rivi);
                      writesel (sel,4, "FOUR PLANETEERS"); writemid (160, 145,rivi);
                      writesel (sel,5, "OOPS!"); writemid (160, 155,rivi);
                      break;
                      
             case 2 : writesel (sel,1, "CLASSIC MAP SIZE"); writemid (160, 115,rivi);
                      writesel (sel,2, "LARGE MAP"); writemid (160, 125,rivi);
                      writesel (sel,3, "OOPS!"); writemid (160, 135,rivi);
                      break;

             case 3 : writesel (sel,1, "12 MONTH GAME"); writemid (160, 115,rivi);
                      writesel (sel,2, "18 MONTH GAME"); writemid (160, 125,rivi);
                      writesel (sel,3, "24 MONTH GAME"); writemid (160, 135,rivi);
                      writesel (sel,4, "OOPS!"); writemid (160, 145,rivi);
                      break;
          }
        
        flip();

        if (noact > 50*30)
          {  demogame = 1;
             game_length = 12;
             mapsize = &defaultmapsizes[0];
             mod_jumptotrack (0);

             prepare_joysticks();
             
             start_new_game ();
             mod_jumptotrack (44);
             ingame = 0;
             reload++;
          }

        if (keybuffer[72] && sel > 1)
          {  keybuffer[72] = 0; sel--; }
          
        if (keybuffer[80] && sel < menuselmax[menu])
          {  keybuffer[80] = 0; sel++; }
          
        if (keybuffer[1])
          {  keybuffer[1] = 0;
             switch (menu)
               {  case 0 : action = 4;
                           break;
                  case 1 : menu = 0; sel = 1;
                           break;
               } // switch menu
          } // if key[1]
          
        if (keybuffer[28])
          {  keybuffer[28] = 0;
             switch (menu)
               { case 0 : // main
                          switch (sel)
                            { case 1 : menu = 1; sel = 1;
                                       break;
                              case 2 : configure_controls(); reload++;
                                       break;
                              case 3 : mule_helpmain(); reload++;
                                       break;
                              case 4 : set_sound_configuration();
                                       mod_load("battlest.s3m");
                                       load_sounds();
                                       mod_start();
                                       mod_jumptotrack (44);
                                       reload++;
                                       break;
                              case 5 : action = 4;
                                       break;
                            }
                           break; // menu0: switch sel

                 case 1 : // player count
                          if (sel == 5)
                            {  menu = 0; sel = 1;   }
                          else
                            {  players = sel;
                               menu = 2;
                             }
                          break;

                 case 2 : // map size
                          if (sel == 3) // oops
                            {  menu = 1; sel = players;
                            }
                          else
                            {  mapsize = &defaultmapsizes[sel-1];
                               menu = 3;
                            }
                          break;

                 case 3 : // game length
                          if (sel == 4) // oops
                            {  menu = 2;
                               for (i = 1; i <= MAPSIZE_LAST; i++)
                                 if (mapsize == &defaultmapsizes[i-1])
                                   {  sel = i; break; }
                            }
                          else
                            {  game_length = 6+sel*6;
                               demogame = 0;
                               mod_jumptotrack (0);
                               prepare_joysticks();
                               start_new_game ();
                               mod_jumptotrack (44);
                               ingame = 0;
                               menu = 0; sel = 1;
                               reload++;
                            }
                          break;
                          
               } // switch menu
          } // if keybuffer 28
     } // while action != 4
}







// *******************************************************************
void load_font (char *name, int nro, int sizeapprox)
{  int w,size,i,o,ysize;
   FILE *tied = ffopen (name, "rb");

   panic ( tied==NULL, name);

   if (font[nro] == NULL)
     font[nro] = (char*) malloc (sizeapprox); // ...too little, perhaps?

   fread ( &ysize,4,1,tied);

   o = 0; i = 32;
   fread ( &w, 4,1, tied);
   while (w)
     {
        fontwidth[nro][i] = w;
        fontofs[nro][i] = o;
        fread ( &size, 4,1, tied);
        fread ( ruutu2, size,1, tied);

        sunpackmem (ruutu2, (font[nro]+fontofs[nro][i]), size);

        o += w*ysize+16;

        fread ( &w, 4,1, tied); i++;
     }

   panic ( o >= sizeapprox, "font_size>approx");

   ffclose (tied);
}

// *******************************************************************
void writef (int x, int y, char *text, int fnro)
{  int l,i;
   unsigned char c;

   l = strlen (text);

   for (i = 0; i < l; i++)
     {
        c = text[i];
        putnpic ( x,y, font[fnro]+fontofs[fnro][c] );
        x += fontwidth[fnro][c]-2;
     }
}

// *******************************************************************
int getflen (char *text, int fnro)
{  int l,i,li;
   unsigned char c;

   l = strlen (text);
   li = 0;

   for (i = 0; i < l; i++)
     {
        c = text[i];
        li += fontwidth[fnro][c]-2;
     }
   return li;
}


// write fontline with it's midpoint at given x coord (y is still at top)
void writemid (int x, int y, char *rivi)
{  writef (x-(getflen(rivi,1) >> 1), y, rivi,1);
}

void writemids (int x, int y, char *rivi)
{  writef (x-(getflen(rivi,2) >> 1), y, rivi,2);
}

// write fontline with it's midpoint at given x coord (y is still at top)
void writeleft (int x, int y, char *rivi)
{  writef (x-getflen(rivi,1), y, rivi,1);
}


void landgrand_timerrout()
{  landgrand_count++;
   if (landgrand_accelerate)
     landgrand_count += 4;
   if (counter) counter--;
   if (sellcounter > 0) sellcounter--;
   if (auctmessagetime) auctmessagetime--;

   if (update_function != NULL)
     update_function();

   if (timetick_speed != -1)
     {  timetick_delay--;
        if (landgrand_accelerate)
          timetick_delay -= 4;
        if (timetick_delay <= 0)
          {  if (sndtickheight == 0)
               sndtickheight = 1;
             else
               sndtickheight = 0;
             playsound (SND_TIMETICK, 22050-sndtickheight*2050+timetick_freq);
             timetick_delay = timetick_speed;
          }
     }
}

// f11-f12-esc
void check_shutdown()
{
   if (keybuffer[87] && keybuffer[88] && keybuffer[1])
     {  disablekeys();
        textmode(3);
        close_sound();
        close_timers();
        exit (0);
     }
     
   if (keybuffer[63])
     keybuffer[63] = 0;

   if ( (keybuffer[1]) && (demogame) )
     {  keybuffer[1] = 0; exitgame = 1; }
   else
     if (keybuffer[1])
       {  ingamemenu();
       }

/*   for (i = 0; i < 4; i++)
     if ( plr_money[i] < 0)
       plr_money[i]--; */

/*   if (keybuffer[59])
     {  keybuffer[59] = 0;
        use_interpolation ^= 1;
     } */
}


int getk(int plr, int key)
{  int joy;
//   if (!plrcontrol) return 0; // computer, n/a

   switch (controltype[plrcontrol[plr]])
     { case 0 : return keybuffer[ (int)( plrkeys[(int)plrcontrol[plr]][key] ) ];
                //break;

       case 1 :
       case 2 : joy = controltype[plrcontrol[plr]]-1;
//                readjoy();

                if (key == K_FIRE)
                  return (joybutt[joy][0] | joybutt[joy][1]);

                if ( (key == K_LEFT) && (joyx[joy] < -30) )
                  return 1;
                if ( (key == K_RIGHT) && (joyx[joy] > 30) )
                  return 1;

                if ( (key == K_UP) && (joyy[joy] < -30) )
                  return 1;
                if ( (key == K_DOWN) && (joyy[joy] > 30) )
                  return 1;

                return 0;
                  
                break;
     }
}

void setk(int plr, int key, int status)
{  if (!plrcontrol) return; // computer, n/a

   if (controltype[plr] == 0)
     keybuffer[ (int)( plrkeys[(int)plrcontrol[plr]][key] ) ] = status;
}

int getkabs(int plr, int key)
{  int joy;
//   if (!plrcontrol) return 0; // computer, n/a

   switch (controltype[plr])
     { case 0 : return keybuffer[ (int)( plrkeys[(int)plr][key] ) ];
                //break;

       case 1 :
       case 2 :
                joy = controltype[plr]-1;
//                readjoy();

                if (key == K_FIRE)
                  return (joybutt[joy][0] | joybutt[joy][1]);

                if ( (key == K_LEFT) && (joyx[joy] < -30) )
                  return 1;
                if ( (key == K_RIGHT) && (joyx[joy] > 30) )
                  return 1;

                if ( (key == K_UP) && (joyy[joy] < -30) )
                  return 1;
                if ( (key == K_DOWN) && (joyy[joy] > 30) )
                  return 1;

                return 0;
                  
                break;
     }
}

void setkabs(int plr, int key, int status)
{  if (!plrcontrol) return; // computer, n/a

   keybuffer[ (int) plrkeys[plr][key] ] = status;
}

void movememo (int x, int y, int xsize, int ysize, char *from, char *to)
{  int o;
   o = x+y*sxsize;
   while (ysize > 0)
     {  memmove ( to+o, from+o, xsize);
        o += sxsize;
        ysize--;
     }
}


void draw_ingamemenu(int sel, char *gamemenu)
{  int i;
   char rivi2[40];

   movememo (80,60,160,80, gamemenu,ruutu);

   writemid (160,68,"- GAME PAUSED -");

   writesel (sel,1,"Return to game");
   writemid (160, 90,rivi);

   i = modvolume * 100 / 64;
   sprintf (rivi2,"Music volume : %i%%",i);
   writesel (sel,2,rivi2);
   writemid (160,100,rivi);

   i = sfxvolume * 100 / 64;
   sprintf (rivi2,"Effect volume : %i%%",i);
   writesel (sel,3,rivi2);
   writemid (160,110,rivi);

   writesel (sel,4,"Quit game");
   writemid (160,120,rivi);
}

void ingamemenu()
{  char *ruututemp,*storespritedest,*gamemenu;
   char storepalette[768];
   timerPFV storetimer;
   int changed,sel;

   if (demogame) { exitgame = 1; return; }
   if (!ingame) return; // not ingame; can't display INGAME menu!
   
   storetimer = timer_routine;
   timer_routine = NULL;
   storespritedest = spritedest;

   memmove (storepalette, gen_palette, 768);

   ruututemp = (char*) malloc (64000);
   gamemenu = (char*) malloc (64000);
   purakuva ("gamemenu.dat", gamemenu);
   memmove (ruututemp, ruutu, 64000);

   changed = 1;
   keybuffer[1] = 0;
   sel = 1;

   while (1)
     {  if (changed)
          {  draw_ingamemenu(sel, gamemenu);
             flip(); changed = 0;
          }
          
        if (keybuffer[1])
          {  keybuffer[1] = 0;
             break;
          }

        if ( (keybuffer[72]) && (sel > 1) )
          {  keybuffer[72] = 0; sel--; changed++; }
        if ( (keybuffer[80]) && (sel < 4) )
          {  keybuffer[80] = 0; sel++; changed++; }

        if (keybuffer[28])
          {  keybuffer[28] = 0;
             if (sel == 1) break; // return to game

             if (sel == 4)
               {  exitgame = 1; break; } // quit game
          }

        if (keybuffer[75])
          {  keybuffer[75] = 0;
             if (sel == 2)
               {  if (modvolume > 0) modvolume -= 2;
                  mod_check_volumes();
                  changed++;
               }
             if (sel == 3)
               {  if (sfxvolume > 0) sfxvolume -= 2;
                  changed++;
               }
          }

        if (keybuffer[77])
          {  keybuffer[77] = 0;
             if (sel == 2)
               {  if (modvolume < 64) modvolume += 2;
                  mod_check_volumes();
                  changed++;
               }
             if (sel == 3)
               {  if (sfxvolume < 64) sfxvolume += 2;
                  changed++;
               }
          }
     }

   timer_routine = storetimer;
   memmove (ruutu, ruututemp, 64000);

   memmove (gen_palette, storepalette, 768);
   setpale (64,64,64);
   
   spritedest = storespritedest;
   free (gamemenu);
   free (ruututemp);

}

